﻿using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;



namespace Mirrors
{

    public class DXMesh
    {
        private Mesh m;
        private Device dev;
        private ExtendedMaterial[] ExtendedMaterials;
        private Texture[] Textures;
        private Material[] Materials;

        public Vector3 center;
        public float radius;
        public Vector3 BoxMin, BoxMax;

        public Vector3 Position;
        public Quaternion Rotation;
        public VertexDeclaration vd;
        public VertexBuffer vb;

        public DXMesh(ref Device d)
        {
            m = null;
            ExtendedMaterials = null;
            Textures = null;
            Materials = null;
            dev = d;
        }

        public void CalculateBounds()
        {
            VertexBuffer vert = m.VertexBuffer;
            GraphicsStream stream = vert.Lock(0, 0, LockFlags.None);
            radius = Geometry.ComputeBoundingSphere(stream, m.NumberVertices, m.VertexFormat, out center);
            Geometry.ComputeBoundingBox(stream, m.NumberVertices, m.VertexFormat, out BoxMin, out BoxMax);
            vert.Unlock();

        }

        public unsafe void Normalize(Matrix PreRotation)  // This method is a dirty hack. It can burn your PC and kill your cat.
        {
            int VertexSize = m.VertexBuffer.Description.Size / m.NumberVertices;
            CalculateBounds();

            GraphicsStream data = m.VertexBuffer.Lock(0, m.VertexBuffer.SizeInBytes, LockFlags.None);
            byte* b = (byte*)data.InternalDataPointer;
            float* f;
            Vector3 V;
            for (int i = 0; i < m.NumberVertices; i++)
            {
                f = (float*)(b + VertexSize * i);
                V = new Vector3(f[0], f[1], f[2]);
                V -= center;
                V.Scale(1 / radius);
                V.TransformCoordinate(PreRotation);
                f[0] = V.X;
                f[1] = V.Y;
                f[2] = V.Z;

            }

            m.VertexBuffer.Unlock();
            CalculateBounds();
        }

        public void NormalizeVB()
        {
            //m = m.Clone(m.Options.Value, CustomVertex.PositionNormalTextured.Format, dev);
            //m.ComputeNormals();
            vd = new VertexDeclaration(dev, m.Declaration);
            vb = m.VertexBuffer;
            m.ComputeNormals();
        }

        public void Render(ref Effect effect, string xTextureName)
        {
            dev.VertexFormat = m.VertexFormat;

            for (int i = 0; i < ExtendedMaterials.Length; i++)
            {
                if (xTextureName != null)
                {
                    effect.SetValue(xTextureName, Textures[i]);
                    effect.CommitChanges();
                }
                m.DrawSubset(i);
            }
        }

        public void RotateY(float angle)
        {
            Vector3 TempVec = new Vector3(0, 1, 0);
            Vector4 V = Vector3.Transform(TempVec, Matrix.RotationQuaternion(Rotation));
            Quaternion TempRot = Quaternion.RotationAxis(new Vector3(V.X, V.Y, V.Z), angle);
            Rotation *= TempRot;
        }

        public void RotateX(float angle)
        {
            Vector3 TempVec = new Vector3(1, 0, 0);
            Vector4 V = Vector3.Transform(TempVec, Matrix.RotationQuaternion(Rotation));
            Quaternion TempRot = Quaternion.RotationAxis(new Vector3(V.X, V.Y, V.Z), angle);
            Rotation *= TempRot;
        }

        public void RotateZ(float angle)
        {
            Vector3 TempVec = new Vector3(0, 0, 1);
            Vector4 V = Vector3.Transform(TempVec, Matrix.RotationQuaternion(Rotation));
            Quaternion TempRot = Quaternion.RotationAxis(new Vector3(V.X, V.Y, V.Z), angle);
            Rotation *= TempRot;
        }

        public void CreateTerrainFromFile(float mappingfactor, float StepX, float StepY, float StepZ, string Filename)
        {
            Bitmap b = new Bitmap(Filename);

            int NoX = b.Size.Width;
            int NoY = b.Size.Height;
            int NoV = NoX * NoY;
            int NoF = (NoX - 1) * (NoY - 1) * 2 * 3;
            CustomVertex.PositionNormalTextured[] vertices = new CustomVertex.PositionNormalTextured[NoV];
            int k = 0;
            for (int i = 0; i < NoX; i++)
                for (int j = 0; j < NoY; j++)
                {
                    vertices[k].Position = new Vector3(i * StepX - (NoX / 2) * StepX, j * StepY - (NoY / 2) * StepY, b.GetPixel(i, j).R * StepZ);
                    vertices[k].Tu = mappingfactor * i / (float)(NoX - 1);
                    vertices[k].Tv = mappingfactor * j / (float)(NoY - 1);
                    vertices[k].Normal = new Vector3(0, 0, 0);
                    k++;
                }
            int[] indices = new int[NoF];

            k = 0;
            for (int i = 1; i < NoX; i++)
                for (int j = 1; j < NoY; j++)
                {
                    indices[k++] = (i - 1) * NoY + j;
                    indices[k++] = (i - 1) * NoY + j - 1;
                    indices[k++] = i * NoY + j - 1;
                    indices[k++] = (i - 1) * NoY + j;
                    indices[k++] = i * NoY + j - 1;
                    indices[k++] = i * NoY + j;
                }

            m = new Mesh(NoF, NoV, MeshFlags.Managed | MeshFlags.Use32Bit, CustomVertex.PositionNormalTextured.Format, dev);


            m.IndexBuffer.SetData(indices, 0, LockFlags.None);
            m.VertexBuffer.SetData(vertices, 0, LockFlags.None);
            m.ComputeNormals();
            CalculateBounds();
            ExtendedMaterials = new ExtendedMaterial[1];
            Textures = new Texture[1];
            Materials = new Material[1];
            ExtendedMaterials[0] = new ExtendedMaterial();
            Textures[0] = null;
            Materials[0] = new Material();
            Materials[0].Ambient = Color.White;
            Materials[0].Diffuse = Color.White;
        }

        public void LoadTexture(int Nr, string FileName)
        {
            if ((Textures != null) && (Textures.Length > Nr))
                Textures[Nr] = TextureLoader.FromFile(dev, FileName);
        }

        public void LoadFromFile(string FileName)
        {
            m = Mesh.FromFile(FileName, MeshFlags.Managed, dev, out ExtendedMaterials);
            CalculateBounds();
            if ((ExtendedMaterials != null) && (ExtendedMaterials.Length > 0))
            {
                Textures = new Texture[ExtendedMaterials.Length];
                Materials = new Material[ExtendedMaterials.Length];
                for (int i = 0; i < ExtendedMaterials.Length; i++)
                {
                    Materials[i] = ExtendedMaterials[i].Material3D;
                    Materials[i].Ambient = Materials[i].Diffuse;
                    if ((ExtendedMaterials[i].TextureFilename != null) && (ExtendedMaterials[i].TextureFilename != string.Empty))
                    {
                        Textures[i] = TextureLoader.FromFile(dev, ExtendedMaterials[i].TextureFilename);
                    }
                }
            }
        }
    }
}

